#ifndef _VECTOR_2I_H
#define _VECTOR_2I_H

#include <math.h>
#include <iosfwd>
#include <limits>
#include "utils.h"


struct Vector2I
{
  int x;
  int y;

  Vector2I():x(0.0),y(0.0){}
  Vector2I(int a, int b):x(a),y(b){}

  //sets x and y to zero
  void Zero(){x=0; y=0;}

  //returns true if both x and y are zero
  bool isZero()const{return (x==0 && y==0);}

  //returns the length of the vector
  inline double    Length()const;

  //returns the squared length of the vector (thereby avoiding the sqrt)
  inline int    LengthSq()const;

  inline void      Normalize();

  inline int    Dot(const Vector2I& v2)const;

  //returns positive if v2 is clockwise of this vector,
  //negative if anticlockwise (assuming the Y axis is pointing down,
  //X axis to right like a Window app)
  inline int       Sign(const Vector2I& v2)const;

  //returns the vector that is perpendicular to this one.
  inline Vector2I  Perp()const;

  //adjusts x and y so that the length of the vector does not exceed max
  inline void      Truncate(int max);

  //returns the distance between this vector and th one passed as a parameter
  inline double    Distance(const Vector2I &v2)const;

  //squared version of above.
  inline double    DistanceSq(const Vector2I &v2)const;

  inline void      Reflect(const Vector2I& norm);

  //returns the vector that is the reverse of this vector
  inline Vector2I  GetReverse()const;


  //we need some overloaded operators
  const Vector2I& operator+=(const Vector2I &rhs)
  {
    x += rhs.x;
    y += rhs.y;

    return *this;
  }

  const Vector2I& operator-=(const Vector2I &rhs)
  {
    x -= rhs.x;
    y -= rhs.y;

    return *this;
  }

  const Vector2I& operator*=(const int& rhs)
  {
    x *= rhs;
    y *= rhs;

    return *this;
  }

  const Vector2I& operator/=(const int& rhs)
  {
    x /= rhs;
    y /= rhs;

    return *this;
  }

  bool operator==(const Vector2I& rhs)const
  {
    return ((x == rhs.x) && (y == rhs.y));
  }

  bool operator!=(const Vector2I& rhs)const
  {
    return (x != rhs.x) || (y != rhs.y);
  }
  enum {clockwise = 1, anticlockwise = -1};
};

//-----------------------------------------------------------------------some more operator overloads
inline Vector2I operator*(const Vector2I &lhs, int rhs);
inline Vector2I operator*(int lhs, const Vector2I &rhs);
inline Vector2I operator-(const Vector2I &lhs, const Vector2I &rhs);
inline Vector2I operator+(const Vector2I &lhs, const Vector2I &rhs);
inline Vector2I operator/(const Vector2I &lhs, int val);
std::ostream& operator<<(std::ostream& os, const Vector2I& rhs);
std::ifstream& operator>>(std::ifstream& is, Vector2I& lhs);


//------------------------------------------------------------------------member functions

//------------------------- Length ---------------------------------------
//
//  returns the length of a 2D vector
//------------------------------------------------------------------------
inline double Vector2I::Length()const
{
  return sqrt(x * x + y * y);
}


//------------------------- LengthSq -------------------------------------
//
//  returns the squared length of a 2D vector
//------------------------------------------------------------------------
inline int Vector2I::LengthSq()const
{
  return (x * x + y * y);
}


//------------------------- Vec2DDot -------------------------------------
//
//  calculates the dot product
//------------------------------------------------------------------------
inline int Vector2I::Dot(const Vector2I &v2)const
{
  return x*v2.x + y*v2.y;
}

//------------------------ Sign ------------------------------------------
//
//  returns positive if v2 is clockwise of this vector,
//  minus if anticlockwise (Y axis pointing down, X axis to right)
//------------------------------------------------------------------------


inline int Vector2I::Sign(const Vector2I& v2)const
{
  if (y*v2.x > x*v2.y)
  { 
    return anticlockwise;
  }
  else 
  {
    return clockwise;
  }
}

//------------------------------ Perp ------------------------------------
//
//  Returns a vector perpendicular to this vector
//------------------------------------------------------------------------
inline Vector2I Vector2I::Perp()const
{
  return Vector2I(-y, x);
}

//------------------------------ Distance --------------------------------
//
//  calculates the euclidean distance between two vectors
//------------------------------------------------------------------------
inline double Vector2I::Distance(const Vector2I &v2)const
{
  int ySeparation = v2.y - y;
  int xSeparation = v2.x - x;

  return sqrt(ySeparation*ySeparation + xSeparation*xSeparation);
}


//------------------------------ DistanceSq ------------------------------
//
//  calculates the euclidean distance squared between two vectors 
//------------------------------------------------------------------------
inline double Vector2I::DistanceSq(const Vector2I &v2)const
{
  int ySeparation = v2.y - y;
  int xSeparation = v2.x - x;

  return ySeparation*ySeparation + xSeparation*xSeparation;
}


//--------------------------- Reflect ------------------------------------
//
//  given a normalized vector this method reflects the vector it
//  is operating upon. (like the path of a ball bouncing off a wall)
//------------------------------------------------------------------------
inline void Vector2I::Reflect(const Vector2I& norm)
{
  *this += 2.0 * this->Dot(norm) * norm.GetReverse();
}

//----------------------- GetReverse ----------------------------------------
//
//  returns the vector that is the reverse of this vector
//------------------------------------------------------------------------
inline Vector2I Vector2I::GetReverse()const
{
  return Vector2I(-this->x, -this->y);
}

inline double Vec2DDistance(const Vector2I &v1, const Vector2I &v2)
{

  double ySeparation = v2.y - v1.y;
  double xSeparation = v2.x - v1.x;

  return sqrt(ySeparation*ySeparation + xSeparation*xSeparation);
}

inline double Vec2DDistanceSq(const Vector2I &v1, const Vector2I &v2)
{

  double ySeparation = v2.y - v1.y;
  double xSeparation = v2.x - v1.x;

  return ySeparation*ySeparation + xSeparation*xSeparation;
}

inline double Vec2DLength(const Vector2I& v)
{
  return sqrt(v.x*v.x + v.y*v.y);
}

inline double Vec2DLengthSq(const Vector2I& v)
{
  return (v.x*v.x + v.y*v.y);
}

//------------------------------------------------------------------------operator overloads
inline Vector2I operator*(const Vector2I &lhs, int rhs)
{
  Vector2I result(lhs);
  result *= rhs;
  return result;
}

inline Vector2I operator*(int lhs, const Vector2I &rhs)
{
  Vector2I result(rhs);
  result *= lhs;
  return result;
}

//overload the - operator
inline Vector2I operator-(const Vector2I &lhs, const Vector2I &rhs)
{
  Vector2I result(lhs);
  result.x -= rhs.x;
  result.y -= rhs.y;
  
  return result;
}

//overload the + operator
inline Vector2I operator+(const Vector2I &lhs, const Vector2I &rhs)
{
  Vector2I result(lhs);
  result.x += rhs.x;
  result.y += rhs.y;
  
  return result;
}

//overload the / operator
inline Vector2I operator/(const Vector2I &lhs, int val)
{
  Vector2I result(lhs);
  result.x /= val;
  result.y /= val;

  return result;
}

///////////////////////////////////////////////////////////////////////////////


//treats a window as a toroid
inline void WrapAround(Vector2I &pos, int MaxX, int MaxY)
{
  if (pos.x > MaxX) {pos.x = 0.0;}

  if (pos.x < 0)    {pos.x = MaxX;}

  if (pos.y < 0)    {pos.y = MaxY;}

  if (pos.y > MaxY) {pos.y = 0.0;}
}

//returns true if the point p is not inside the region defined by top_left
//and bot_rgt
inline bool NotInsideRegion(Vector2I p,
                            Vector2I top_left,
                            Vector2I bot_rgt)
{
  return (p.x < top_left.x) || (p.x > bot_rgt.x) || 
         (p.y < top_left.y) || (p.y > bot_rgt.y);
}

inline bool InsideRegion(Vector2I p,
                         Vector2I top_left,
                         Vector2I bot_rgt)
{
  return !((p.x < top_left.x) || (p.x > bot_rgt.x) || 
         (p.y < top_left.y) || (p.y > bot_rgt.y));
}

inline bool InsideRegion(Vector2I p, int left, int top, int right, int bottom)
{
  return !( (p.x < left) || (p.x > right) || (p.y < top) || (p.y > bottom) );
}


#endif